﻿/** AudioManager.cs - A simple Audio Manager for the playback of audio files
 * @author - Spencer Evans
 * @attribution - Similar to a class found in the Roleplaying starter pack on the 
 *  XNA Creators Club Online website (http://creators.xna.com).
 */


#region Using Statements
using System;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using System.Collections.Generic;
#endregion

namespace BenevolentSun
{
    /** A singleton AudioManager class that should only have static methods
     * accessible to ensure instance singularity.
     */
    public class AudioManager : GameComponent
    {
        #region Singleton
        /** The singleton instance for this type. */
        private static AudioManager audioManager = null;

        /** Clean up the component when it is disposing.
         */
        protected override void Dispose(bool disposing)
        {
            try
            {
                if (disposing)
                {
                    StopMusic();
                    if (soundBank != null)
                    {
                        soundBank.Dispose();
                        soundBank = null;
                    }
                    if (waveBank != null)
                    {
                        waveBank.Dispose();
                        waveBank = null;
                    }
                    if (audioEngine != null)
                    {
                        audioEngine.Dispose();
                        audioEngine = null;
                    }
                }
            }
            finally
            {
                base.Dispose(disposing);
            }
        }
        #endregion


        #region Audio Data
        /** The audio engine used to play all cues. */
        private AudioEngine audioEngine;
        /** The soundbank that contains all cues. */
        private SoundBank soundBank;
        /** The wavebank with all wave files for this game. */
        private WaveBank waveBank;
        #endregion


        #region Initialization
        /** Constructs the manager for audio playback of all cues.
         * @param game - The game that this component will be attached to.
         * @param settingsFile - The filename of the XACT settings file.
         * @param waveBankFile - The filename of the XACT wavebank file.
         * @param soundBankFile - The filename of the XACT soundbank file.
         */
        private AudioManager(Game game, string settingsFile, string waveBankFile, string soundBankFile)
            : base(game)
        {
            try
            {
                audioEngine = new AudioEngine(settingsFile);
                waveBank = new WaveBank(audioEngine, waveBankFile);
                soundBank = new SoundBank(audioEngine, soundBankFile);
            }
            catch (NoAudioHardwareException)
            {
                // silently fall back to silence
                audioEngine = null;
                waveBank = null;
                soundBank = null;
            }
        }

        /** Initialize the static AudioManager functionality.
         * @param game - The game that this component will be attached to.
         * @param settingsFile - The filename of the XACT settings file.
         * @param waveBankFile - The filename of the XACT wavebank file.
         * @param soundBankFile - The filename of the XACT soundbank file.
         */
        public static void Initialize(Game game, string settingsFile, string waveBankFile, string soundBankFile)
        {
            audioManager = new AudioManager(game, settingsFile, waveBankFile, soundBankFile);
            if (game != null)
            {
                game.Components.Add(audioManager);
            }
        }
        #endregion


        #region Update Methods
        /** Update the audio manager, particularly the engine.
         */
        public override void Update(GameTime gameTime)
        {
            // update the audio engine
            if (audioEngine != null)
            {
                audioEngine.Update();
            }

            //if ((musicCue != null) && musicCue.IsStopped)
            //{
            //    AudioManager.PopMusic();
            //}

            base.Update(gameTime);
        }
        #endregion


        #region Cue Methods
        /** Retrieve a cue by name.
         * @param cueName - The name of the cue requested.
         * @return The cue corresponding to the name provided.
         */
        public static Cue GetCue(string cueName)
        {
            if (String.IsNullOrEmpty(cueName) ||
                (audioManager == null) || (audioManager.audioEngine == null) ||
                (audioManager.soundBank == null) || (audioManager.waveBank == null))
            {
                return null;
            }
            return audioManager.soundBank.GetCue(cueName);
        }
        /** Plays a cue by name.
         * @param name - The name of the cue to play.
         */
        public static void PlayCue(string cueName)
        {
            if ((audioManager != null) && (audioManager.audioEngine != null) &&
                (audioManager.soundBank != null) && (audioManager.waveBank != null))
            {
                audioManager.soundBank.PlayCue(cueName);
            }
        }
        /** Plays a cue by name.
         * @param name - The name of the cue to play.
         */
        public static void PlayCue(string cueName, AudioListener listener, AudioEmitter emitter)
        {
            if ((audioManager != null) && (audioManager.audioEngine != null) &&
                (audioManager.soundBank != null) && (audioManager.waveBank != null))
            {
                audioManager.soundBank.PlayCue(cueName, listener, emitter);
            }
        }
        #endregion


        #region Music
        float musicVol = 1f;

        public static void SetMusicVolume(float vol)
        {
            audioManager.musicVol = vol;
            audioManager.audioEngine.GetCategory("Music").SetVolume(vol);
        }

        

        /** The cue for the music currently playing, if any. */
        private Cue musicCue;
        /** Stack of music cue names, for layered music playback. */
        private Stack<string> musicCueNameStack = new Stack<string>();

        public static String CurrentMusic()
        {
            if (audioManager.musicCueNameStack.Count > 0)
                return audioManager.musicCueNameStack.Peek();
            return null;
        }

        /** Plays the desired music, clearing the stack of music cues.
         * @param cueName - The name of the music cue to play.
         */
        public static void PlayMusic(string cueName)
        {
            // start the new music cue
            if (audioManager != null)
            {
                audioManager.musicCueNameStack.Clear();
                PushMusic(cueName);
            }
        }

        /** Plays the music for this game, adding it to the music stack.
         * @param cueName - The name of the music cue to play.
         */
        public static void PushMusic(string cueName)
        {
            // Make sure we are initialized
            if ((audioManager == null) || (audioManager.audioEngine == null) ||
                (audioManager.soundBank == null) || (audioManager.waveBank == null))
                return;

            // Push on the musit cue
            audioManager.musicCueNameStack.Push(cueName);

            //  If no music is playing or some music other than this playing, start it!
            if ((audioManager.musicCue == null) || (audioManager.musicCue.Name != cueName))
            {
                // Stop the current music if there is any
                if (audioManager.musicCue != null)
                {
                    audioManager.musicCue.Stop(AudioStopOptions.AsAuthored);
                    audioManager.musicCue.Dispose();
                    audioManager.musicCue = null;
                }

                // Start the new music
                audioManager.musicCue = GetCue(cueName);
                if (audioManager.musicCue != null)
                {
                    audioManager.musicCue.Play();
                }
            }
        }

        /** Stops the current music and plays the previous music on the stack.
         */
        public static void PopMusic()
        {
            // Make sure we are initialized
            if ((audioManager == null) || (audioManager.audioEngine == null) ||
                (audioManager.soundBank == null) || (audioManager.waveBank == null))
                return;


            string cueName = null;
            if (audioManager.musicCueNameStack.Count > 0)
            {
                // Pop off the old music
                audioManager.musicCueNameStack.Pop();

                // If there is another song, grab a reference to it
                if (audioManager.musicCueNameStack.Count > 0)
                {
                    cueName = audioManager.musicCueNameStack.Peek();
                }
            }

            //  If no music is playing or some music other than this playing stop it,  and start the new music!
            if ((audioManager.musicCue == null) || (audioManager.musicCue.Name != cueName))
            {
                // Stop the current music if there is any
                if (audioManager.musicCue != null)
                {
                    audioManager.musicCue.Stop(AudioStopOptions.AsAuthored);
                    audioManager.musicCue.Dispose();
                    audioManager.musicCue = null;
                }

                // Start the new music
                if (!String.IsNullOrEmpty(cueName))
                {
                    audioManager.musicCue = GetCue(cueName);
                    if (audioManager.musicCue != null)
                    {
                        audioManager.musicCue.Play();
                    }
                }
            }
        }

        /** Stop music playback, clearing the cue.
         */
        public static void StopMusic()
        {
            if (audioManager != null)
            {
                // Clear the cue
                audioManager.musicCueNameStack.Clear();

                // Stop any playing music
                if (audioManager.musicCue != null)
                {
                    audioManager.musicCue.Stop(AudioStopOptions.AsAuthored);
                    audioManager.musicCue.Dispose();
                    audioManager.musicCue = null;
                }
            }
        }
        #endregion
    }
}
